package com.chain.qs.modules.biz.listener;

import com.chain.qs.common.config.Global;
import com.chain.qs.common.mapper.JsonMapper;
import com.chain.qs.common.utils.CacheUtils;
import com.chain.qs.common.utils.CastUtils;
import com.chain.qs.common.utils.EhCacheUtils;
import com.chain.qs.common.utils.StringUtils;
import com.chain.qs.modules.biz.dp.handler.AbstractDPHandler;
import com.chain.qs.modules.biz.entity.WarningRecord;
import com.chain.qs.modules.biz.service.BasicInfoService;
import com.chain.qs.modules.biz.service.WarningRecordService;
import com.google.common.base.Predicate;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.apache.activemq.command.ActiveMQTopic;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;
import java.util.*;

/**
 * 数据获取监听器(原始设备采集数据)
 * 从mq中获取
 */
@Component
public class ConsumerMessageListener implements MessageListener {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Value("${disconnectLevel}")
    private String disconnectLevel; //4

    @Value("${disconnectRemarks}")
    private String disconnectRemarks;//设备断线

    @Resource
    private BasicInfoService basicInfoService;

    @Resource
    private WarningRecordService warningRecordService;

    @Resource(name = "jmsTemplate")
    private JmsTemplate jmsTemplate;

    @Resource(name = "wnTopicDestination")
    private ActiveMQTopic wnTopicDestination;

    @Resource
    private Map<String, AbstractDPHandler> operationServiceMap =  AbstractDPHandler.dpHandlerMap;

    @Resource
    private SimpMessagingTemplate msgTemplate;


    private TextMessage recievedMsg;
    private String json = "";

    private Map<String,HashSet<String>> existRule;
    private Set<String> tempRule;

    private String sKey;            //officeCode
    private String gKey = "";       //sgroupId
    private String dKey = "";       //deviceId
    private String pKey = "";       //paramId;
    private String rKey = "";       //ruleId
    private String deviceTypeKey = "";       //设备类型ID
    private Boolean sendBoolean;    //true:发送到中间件；false:异常没有变，不发送到中间件
    private String supStationCode = "";    //

    public void onMessage(Message message) {
        //初始化各变量
        resetVariable();
        recievedMsg = (TextMessage) message;
        try {
            json = recievedMsg.getText();
            logger.debug("This is receive jsonString : " + json);
        } catch (JMSException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
//        json = "{\"100000001001\":{\"58\":{\"36\":{\"m_2_crcy_8\":\"0.0\",\"ou_pw\":\"0.80\",\"m_2_crcy_7\":\"2.6\",\"m_1_vol\":\"32.0\",\"m_2_crcy_9\":\"0.0\",\"m_1_crcy_1\":\"3.8\",\"m_2_ou_pw\":\"0.28\",\"m_2_crcy_4\":\"4.5\",\"m_2_crcy_3\":\"5.0\",\"m_2_crcy_6\":\"4.0\",\"m_2_crcy_5\":\"3.5\",\"m_1_crcy_5\":\"1.7\",\"m_1_crcy_4\":\"4.0\",\"m_1_crcy_10\":\"0.0\",\"m_1_crcy_3\":\"4.8\",\"m_2_crcy_2\":\"4.0\",\"m_1_crcy_2\":\"4.4\",\"m_2_crcy_1\":\"2.9\",\"m_1_crcy_9\":\"0.0\",\"m_1_crcy_8\":\"0.0\",\"m_1_crcy_7\":\"2.1\",\"m_1_crcy_6\":\"3.2\",\"m_2_vol\":\"32.8\",\"m_2_crcy_10\":\"0.0\",\"m_2_rf_pw\":\"0.00\",\"m_1_temp\":\"17.00\",\"m_1_ou_pw\":\"0.57\",\"m_1_rf_pw\":\"9.00\",\"m_2_temp\":\"16.00\",\"rf_pw\":\"0.00\"},\"37\":{}}}}";
        Map<String, String> map = CastUtils.convertToHashMapStringString(JsonMapper.fromJsonString(json, Map.class));
        httpSendMsg(map);
        if (map != null && map.size() > 0) {
            for (Map.Entry<String, String> stationEntry : map.entrySet()) {// 站级
                stationMapOperate(stationEntry);
            }
            isWarningMark();
            // 清除warningMap中过时的告警信息
            recoveryWarning();
            if (sendBoolean) {
                for (Map.Entry<String, AbstractDPHandler> entry : operationServiceMap.entrySet()) {
//                    System.out.println("application runnable entry.getKey() = " + entry.getKey());
                    AbstractDPHandler handler = entry.getValue();
                    handler.saveDataToDB();
                }
                EhCacheUtils.put(EhCacheUtils.NOT_RECOVERY_WARNING_RECORD_RULE_MAP_INFO_CACHE, warningRecordService.getExistRuleMap());

                if("true".equalsIgnoreCase(Global.getConfig("is.production"))){
                    jmsTemplate.convertAndSend(wnTopicDestination, JsonMapper.toJsonString(EhCacheUtils.get(EhCacheUtils.NOT_RECOVERY_WARNING_RECORD_RULE_MAP_INFO_CACHE)));
                }
//                System.out.println("send json:" + JsonMapper.toJsonString(EhCacheUtils.get(EhCacheUtils.NOT_RECOVERY_WARNING_RECORD_RULE_MAP_INFO_CACHE)));
            }
        }
    }

    /**
     * @param stationEntry :: code={g={d={p=v},d={falg=y}}}
     */
    private void stationMapOperate(Map.Entry<String, String> stationEntry) {
        sKey = stationEntry.getKey();
        if("".equals(supStationCode)) {
            supStationCode = sKey.substring(0, sKey.length() - 3);
        }
        Map<String, String> stationMap = CastUtils.convertToHashMapStringString(stationEntry.getValue());
        for (Map.Entry<String, String> sgroupEntry : stationMap.entrySet()) {// 组级
            groupMapOperate(sgroupEntry);
        }
    }

    /**
     * @param groupEntry :: g={d={p=v},d={falg=y}}
     */
    private void groupMapOperate(Map.Entry<String, String> groupEntry) {
        gKey = groupEntry.getKey();
        Map<String, Object> groupMap = CastUtils.convertToHashMap(groupEntry.getValue());
        for (Map.Entry<String, Object> deviceEntry : groupMap.entrySet()) {// 设备级
            deviceMapOperate(deviceEntry);
        }
    }

    /**
     * @param deviceEntry :: d={p=v} // d={falg=y}
     */
    private void deviceMapOperate(Map.Entry<String, Object> deviceEntry) {
        dKey = deviceEntry.getKey();
        Map<String, Object> deviceMap = CastUtils.convertToHashMap(deviceEntry.getValue());
        deviceTypeKey = basicInfoService.findAvailableDeviceIdAndTypeId().get(dKey);
        if (deviceMap.size() > 0) {//设备中有参数，即设备没有中断
            handlerCacheOperate(deviceMap);//对每个设备进行记录
            for (Map.Entry<String, Object> paramEntry : deviceMap.entrySet()) {//参数级，eg.pow=-10.00 \n rf_sw=0 \n freq=0 \n temp=0.0 \n net_mode=0
                paramsWarnningCompare(paramEntry); //
            }
        } else {//设备参数个数为零，即设备中断
            pKey = basicInfoService.findAvailableParam().get("0@" + deviceTypeKey);//每个设备类型参数中都额外添加一个参数，并规定kid=0
            if (pKey != null) {
                String slgdprKey = keyAppend(new String[]{sKey, disconnectLevel, gKey, dKey, pKey, "0"});
                tempRule.add(slgdprKey);
                if(existRule.get(supStationCode) == null){
                    existRule.put(supStationCode, Sets.newHashSet());
                }
                if (!existRule.get(supStationCode).contains(slgdprKey)) {
                    if (!sendBoolean) {
                        sendBoolean = true;
                    }
                    existRule.get(supStationCode).add(slgdprKey);
                    warningRecordService.saveNewRule(sKey, disconnectLevel, gKey, dKey, pKey, "0", "", disconnectRemarks);
                }
            }
        }
    }

    /**
     * 设备参数与告警规则判断
     *
     * @param paramEntry e.g.pow=10
     */
    private void paramsWarnningCompare(Map.Entry<String, Object> paramEntry) {
        if (basicInfoService.findAvailableParam().get(paramEntry.getKey() + "@" + deviceTypeKey) != null) {
            pKey = basicInfoService.findAvailableParam().get(paramEntry.getKey() + "@" + deviceTypeKey);
            String gdpKey = keyAppend(new String[]{gKey, dKey, pKey});
            if (basicInfoService.warningRuleMapInfoCache().get(gdpKey) != null) {//站组下设备的参数在规则map中有规则
                String paramValue = paramEntry.getValue().toString();//设备的参数的值
                for (Map.Entry<String, String> ruleOfParam : basicInfoService.warningRuleMapInfoCache().get(gdpKey).entrySet()) {//该参数的规则
                    // 与一条规则比较
                    if (ruleCompare(ruleOfParam, paramValue)) {
                        break;//不再继续与该参数的其它规则比较
                    }
                }
            }
        } else {
            System.out.println("参数不存在...");
        }
    }


    /**
     * 参数值与一条规则比较
     *
     * @param ruleOfParam      ruleOfParam.getKey() = "sgroupId@deviceId@paramId@ruleId"
     * @param paramValueString 参数值
     */
    private boolean ruleCompare(Map.Entry<String, String> ruleOfParam, String paramValueString) {
        double paramValue = Double.parseDouble(paramValueString);
        rKey = ruleOfParam.getKey().split("@")[3];
        String conditionString = ruleOfParam.getValue();//取得该map的规则
        String[] valueArray = conditionString.split("@");
        String comparisonOperator = valueArray[0];
        double conditionValue = Double.parseDouble(valueArray[1]);
        String relationOperator = valueArray[2];
        boolean condition1Boolean;//第一个条件判断结果
        boolean condition2Boolean;//第二个条件判断结果
        boolean result;//最终判断结果：第一个条件判断结果(&&或||)第二个条件判断结果
        condition1Boolean = oneConditionOfRule(comparisonOperator, paramValue, conditionValue);
        if (relationOperator.equals("")) {//逻辑条件不存在，即第2个判断条件不存在
            result = condition1Boolean;//第一个判断结果就是最终结果
        } else {
            comparisonOperator = valueArray[3];
            conditionValue = Double.parseDouble(valueArray[4]);
            condition2Boolean = oneConditionOfRule(comparisonOperator, paramValue, conditionValue);
            if (relationOperator.equals("0")) {//逻辑条件：0：且；1：或。
                result = (condition1Boolean && condition2Boolean);
            } else {
                result = (condition1Boolean || condition2Boolean);
            }
        }
        if (result) {//符合该条件
            operateRule(valueArray[5], valueArray[6]);
        }
        return result;
    }

    /**
     * 比较一条规则中的一个条件
     *
     * @param comparisonOperator 比较符
     * @param paramValue         参数值
     * @param conditionValue     条件值
     * @return 比较结果
     */
    private boolean oneConditionOfRule(String comparisonOperator, double paramValue, double conditionValue) {
        switch (comparisonOperator) {
            case "0"://0：小于等于
                return paramValue <= conditionValue;
            case "1"://1：小于
                return paramValue < conditionValue;
            case "2"://2：大于等于
                return paramValue >= conditionValue;
            case "3"://3：大于
                return paramValue > conditionValue;
            default:
                return false;
        }
    }

    //根据规则的判断结果进行操作
    private void operateRule(String level, String remarks) {
        String slgdprKey = keyAppend(new String[]{sKey, level, gKey, dKey, pKey, rKey});
        tempRule.add(slgdprKey);
        if (!existRule.get(supStationCode).contains(slgdprKey)) {
            if (!sendBoolean) {
                sendBoolean = true;
            }
            existRule.get(supStationCode).add(slgdprKey);
            warningRecordService.saveNewRule(sKey, level, gKey, dKey, pKey, rKey, "", remarks);
        }
    }

    /**
     * 若设备的参数中至少有一个参数的值发生告警，则设置设备的标识isWarning=true
     * isWarning=true:新接收的信息表明该设备告警信息发生变化
     * isWarning=false:新接收的信息表明该设备告警信息没有发生变化
     */
    private void isWarningMark() {
        if (tempRule.size() > 0) {
            for (String slgdprKey : tempRule) {
                handlerWarningUpdate(slgdprKey);
            }
        }
    }

    // 清除warningMap中过时的告警信息
    private void recoveryWarning() {
        WarningRecord warningRecord = new WarningRecord();
        String[] slgdprArray;
        Set delList = new HashSet();
        if(existRule.get(supStationCode) != null){
            for (String slgdprKey : existRule.get(supStationCode)) {
                if (!tempRule.contains(slgdprKey)) {
                    if (!sendBoolean) {
                        sendBoolean = true;
                    }
                    slgdprArray = slgdprKey.split("@");
                    warningRecord.setOfficeCode(slgdprArray[0]);
                    warningRecord.setIsSuspend(slgdprArray[1]);
                    warningRecord.setSgroupId(slgdprArray[2]);
                    warningRecord.setItemId(slgdprArray[3]);
                    warningRecord.setParamId(slgdprArray[4]);
                    warningRecord.setRuleId(slgdprArray[5]);
                    warningRecordService.recovyRuleRecord(warningRecord);
                    handlerWarningUpdate(slgdprKey);
                    delList.add(slgdprKey);
                }
            }
            existRule.get(supStationCode).removeAll(delList);
            EhCacheUtils.put(EhCacheUtils.NOT_RECOVERY_WARNING_RECORD_RULE_SET_IN_MAP_INFO_CACHE, existRule);
        }
    }


    private String keyAppend(String[] keyArray) {
        StringBuilder sb = new StringBuilder();
        if (keyArray != null && keyArray.length > 0) {
            sb.append(keyArray[0]);
            for (int i = 1; i < keyArray.length; i++) {
                sb.append("@").append(keyArray[i]);
            }
        }
        return sb.toString();
    }

    /**
     * 保存设备的参数信息 并 设置标识为true(true:可进行插入操作；false：不可进行插入操作)
     *
     * @param deviceMap 包含设备id和设备的参数名、参数值信息
     */
    private void handlerCacheOperate(Map<String, Object> deviceMap) {
        String keyword = basicInfoService.findAvailableDeviceIdKeyword().get(dKey);
        AbstractDPHandler handler = operationServiceMap.get(keyword);
        if (handler != null) {
            handler.setOfficeCode(sKey);
            handler.setSgroupId(gKey);
            handler.setDeviceId(dKey);
            handler.updateCache(deviceMap);
        }
    }

    /**
     * 修改标识值为true
     * true:设备的告警信息发生变化，需要进行记录;
     * false:设备的告警信息发生变化，可以忽略不记.
     *
     * @param slgdprKey 设备ID
     */
    private void handlerWarningUpdate(String slgdprKey) {
        dKey = slgdprKey.split("@")[3];
        // 设备关键字
        String keyword = basicInfoService.findAvailableDeviceIdKeyword().get(dKey);
        // 设备信息处理类
        AbstractDPHandler handler = operationServiceMap.get(keyword);
        String osd =  slgdprKey.split("@")[0]+"@"+slgdprKey.split("@")[2]+"@"+slgdprKey.split("@")[3];
        if (handler != null && !handler.isWarning(osd)) {
            handler.setWarning(osd,true);
        }
    }

    public void setOperationServiceMap(Map<String, AbstractDPHandler> operationServiceMap) {
        this.operationServiceMap = operationServiceMap;
    }

    public void setJmsTemplate(JmsTemplate jmsTemplate) {
        this.jmsTemplate = jmsTemplate;
    }

    /**
     * 初始化各变量（每次来新消息时执行）
     */
    private void resetVariable() {
        existRule = warningRecordService.getExistRuleSet();
        tempRule = new HashSet<>();
        sKey = "";//officeCode
        gKey = "";//sgroupId
        dKey = "";//deviceId
        pKey = "";//paramId
        rKey = "";//ruleId
        deviceTypeKey = "";//设备类型ID
        sendBoolean = false;//true:发送到中间件；false:异常没有变，不发送到中间件
        supStationCode = "";
    }

    public void httpSendMsg(Map<String, String> dataMap) {
        Map<String, String> wsSessionMap = (Map<String, String>)CacheUtils.get("ws_sessions");
        wsSessionMap = (wsSessionMap == null ? Maps.newConcurrentMap() : wsSessionMap);
        Map<String, String> filteredDateMap = null;
        Object tmpMsgDate = "";
        if (wsSessionMap != null) {
            Set<String> set = wsSessionMap.keySet();
            Iterator<String> iterator = set.iterator();
            while (iterator.hasNext()) { //遍历根据用户进行推送
                String sessionId = iterator.next();
//                this.msgTemplate.convertAndSendToUser(key, "/queue/original", msg);
//                String tmpCacheSessionStaionCode = wsSessionMap.get(sessionId);
//                if(!StringUtils.isBlank(tmpCacheSessionStaionCode)){
//                    tmpMsgDate = dataMap.get(tmpCacheSessionStaionCode);

//                    //过滤map 方式一(采用guava)
//                    filteredDateMap = Maps.filterKeys(dataMap, new Predicate<String>() {
//                        @Override
//                        public boolean apply(String input) {
//                            //过滤Map中键值包含字母 tmpCacheSessionStaionCode 的元素
//                            return input.equals(tmpCacheSessionStaionCode);
//                        }
//                    });

//                    if(tmpMsgDate != null){
                        this.msgTemplate.convertAndSend("queue/original-user"+sessionId,dataMap);
//                    }
//                }
            }
        }
    }
}